package com.atguigu.srb.core.service.impl;

import com.atguigu.common.exception.Assert;
import com.atguigu.common.exception.BusinessException;
import com.atguigu.common.results.ResultCodeEnum;
import com.atguigu.srb.base.utils.JwtUtils;
import com.atguigu.srb.core.hfb.FormHelper;
import com.atguigu.srb.core.hfb.HfbConst;
import com.atguigu.srb.core.hfb.RequestHelper;
import com.atguigu.srb.core.pojo.entity.UserBind;
import com.atguigu.srb.core.mapper.UserBindMapper;
import com.atguigu.srb.core.pojo.entity.UserInfo;
import com.atguigu.srb.core.pojo.vo.UserBindVo;
import com.atguigu.srb.core.service.UserBindService;
import com.atguigu.srb.core.service.UserInfoService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;

/**
 * <p>
 * 用户绑定表 服务实现类
 * </p>
 *
 * @author Atguigu
 * @since 2022-10-24
 */
@Service
@Slf4j
public class UserBindServiceImpl extends ServiceImpl<UserBindMapper, UserBind> implements UserBindService {

    @Override
    public String accountBind(String token, UserBindVo userBindVo) {
        Long userId = JwtUtils.getUserId(token);
        //判断用户之前是否已经绑定过： 身份证是否已被占用、userId的用户是否存在绑定数据
        //1、身份证是否已被占用
        String idCard = userBindVo.getIdCard();
        int count = this.count(new LambdaQueryWrapper<UserBind>()
                .eq(UserBind::getIdCard, idCard));
        if(count>0){
            //已被占用
            throw new BusinessException(ResultCodeEnum.USER_BIND_IDCARD_EXIST_ERROR);
        }
        //2、userId的用户是否存在绑定数据
        UserBind userBind = this.getOne(new LambdaQueryWrapper<UserBind>()
                .eq(UserBind::getUserId, userId));
        if(userBind!=null){
            //2.2 用户已存在绑定数据  更新绑定
            BeanUtils.copyProperties(userBindVo , userBind);
            this.updateById(userBind);
        }else{
            //用户没有绑定数据
            //2、1保存用户绑定数据到数据库
            userBind = new UserBind();
            BeanUtils.copyProperties(userBindVo,userBind);
            userBind.setUserId(userId);
            //绑定状态（0：未绑定，1：绑定成功 -1：绑定失败）
            userBind.setStatus(0);
            this.save(userBind);
        }
        //userBind本地一定保存/更新成功
        //3、将绑定数据提交给hfb完成账户绑定
        //汇付宝接受账户绑定的接口地址：http://localhost:9999/userBind/BindAgreeUserV2  请求方式post
        //传参1： agentId：商户在hfb注册时生成的id
        //参数2： agentUserId： 要绑定账户的会员的id
        //参数3、4： idCard 身份证号  personalName 姓名
        //参数5、6： bankType 银行卡类型   bankNo 银行卡号
        //参数7： mobile 手机号码
        //参数8/9 ： returnUrl(同步地址 用户绑定后希望用户浏览器直接去访问的接口)
        //          notifyUrl(异步地址 hfb访问的通知用户绑定结果的接口)
        //参数10： timestamp  时间戳
        //参数11 ：  sign  基于上面的数据生成的签名(保证数据传递给hfb是安全的)
        //3.1 导入hfb提供的工具类到项目中： 第四部分 java代码/将 service-core的hfb目录拷贝到项目中
        //                                  再将guigu-common util目录下的HttpUtils拷贝到hfb目录下
        //3.2 HfbConst: 封装了hfb的一些参数    RequestHelper 签名参数处理相关的工具类
        //3.3 封装请求参数：Map
        Map map = new HashMap<String,Object>();
        map.put("agentId",HfbConst.AGENT_ID);
        map.put("agentUserId",userId);
        map.put("idCard",userBindVo.getIdCard());
        map.put("personalName",userBindVo.getName());
        map.put("bankType",userBindVo.getBankType());
        map.put("bankNo",userBindVo.getBankNo());
        map.put("mobile",userBindVo.getMobile());
        map.put("returnUrl",HfbConst.USERBIND_RETURN_URL);
        map.put("notifyUrl",HfbConst.USERBIND_NOTIFY_URL);
        map.put("timestamp",System.currentTimeMillis());
        map.put("sign", RequestHelper.getSign(map));

        //3.4 发送请求:
        //   方式1：     HttpUtils.doPost(HfbConst.USERBIND_URL,data);  传参 参数需要使用GBK编码处理
        // 方式2： 使用表单将参数提交给hfb不需要处理参数
        //遍历上面的map生成表单字符串： <form  method="post" action="HfbConst.USERBIND_URL">
        //  <input name="agentId" value=HfbConst.AGENT_ID>    </form>
        // <script> document.form.submit() </script>
        //将参数封装到一个自动提交的表单页面字符串中  响应给浏览器，浏览器只需要解析结果会自动将表单数据提交给hfb绑定接口
        String formStr = FormHelper.buildForm(HfbConst.USERBIND_URL, map);
        return formStr;
    }
    @Autowired
    UserInfoService userInfoService;
    @Transactional(rollbackFor = {Exception.class})
    @Override
    public void bindNotify(HttpServletRequest request) {

        //获取hfb返回的参数
        Map<String, String[]> parameterMap = request.getParameterMap();
        //将k-v[] 的map转为 k v[0] 的map
        Map<String, Object> map = RequestHelper.switchMap(parameterMap);
        //验证签名是否正确
        boolean flag = RequestHelper.isSignEquals(map);
        if(!flag){ //签名验证失败
            log.error("签名验证失败：{}" , map);
            //修改userInfo和userBind的status=-1
            throw new BusinessException(ResultCodeEnum.ERROR);
        }
        //验证绑定是否成功
        //{agentUserId=10, resultCode=0001, bindCode=4250673993224e05b18b66d5510c3334, resultMsg=成功, timestamp=1667529854694}
        String agentUserId = map.get("agentUserId").toString();
        String resultCode = map.get("resultCode").toString();
        String bindCode = map.get("bindCode").toString();
        if(!"0001".equals(resultCode)){
            log.error("绑定失败：{}" , map);
            //设置绑定失败的状态
            throw new BusinessException(ResultCodeEnum.ERROR);
        }
        //根据用户绑定结果 修改service-core 的用户的绑定状态和数据
        //更新绑定表数据：user_bind
        UserBind userBind = this.getOne(new LambdaQueryWrapper<UserBind>()
                .eq(UserBind::getUserId, agentUserId));
        userBind.setBindCode(bindCode);
        userBind.setStatus(1);//修改为绑定成功
        this.updateById(userBind);
        //更新用户表中的绑定状态
        UserInfo userInfo = userInfoService.getById(agentUserId);
        userInfo.setIdCard(userBind.getIdCard());
        userInfo.setName(userBind.getName());
        userInfo.setBindStatus(1);
        userInfo.setBindCode(bindCode);
        userInfoService.updateById(userInfo);
    }
}
